package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"pegasus/controllers"
	"pegasus/datatypes/protocol"
	"pegasus/middle"
	"pegasus/models"
	"pegasus/repositories"
	"pegasus/services"
	"pegasus/utils"
	"reflect"
	"time"

	"github.com/dalixu/owig/owmw"
	"github.com/gorilla/websocket"

	"github.com/dalixu/ginjector"
	"github.com/dalixu/owig"

	_ "github.com/go-sql-driver/mysql"
	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"
)

//Config 文件配置 对应pegasus.json
var configPath = "./pegasus.json"
var logConfigPath = "./glog.json"

//Config 文件配置
type Config struct {
	GLog             bool
	DBDriver         string
	DataSource       string
	WebIP            string
	ServerQueueCache int
	ClientQueueCache int
}

func main() {
	config, err := loadConfig()
	if err != nil {
		panic(err)
	}
	if config.GLog {
		utils.Startup(logConfigPath)
	}
	engine := syncDB(config)
	builder := createBuilder(config, engine)
	startJob(engine)
	startServer(config, builder)
}

func startJob(engine *xorm.Engine) {
	//启动任务
	chs := services.NewCharacterService(utils.GetLogger("CharacterService"), engine)
	chs.UpdatePeriod()
}

func createBuilder(config *Config, engine *xorm.Engine) ginjector.Builder {
	//配置依赖注入
	builder := ginjector.New()
	//websocket参数
	builder.RegisterType(&websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin:     func(r *http.Request) bool { return true }, //暂时允许websocket跨域
	})
	//builder.Register(func(c ginjector.Container) interface{} { return engine }).Adapter(&xorm.Engine{}).SingleInstance()
	builder.Register(func(c ginjector.Container) interface{} { return services.NewSessionService() }).
		As(reflect.TypeOf((*services.SessionService)(nil)).Elem()).SingleInstance()

	//HubService由于是把链接信息放到内存中 所以只能注册为单键
	builder.RegisterType(services.NewHubService(utils.GetLogger("HubService"), &protocol.JSONProtocol{}, config.ServerQueueCache, config.ClientQueueCache))
	//userRepo目前只是用了数据库链接 所以可以每次都创建
	builder.Register(func(c ginjector.Container) interface{} {
		return repositories.NewUserRepo(utils.GetLogger("UserRepo"), engine)
	}).Adapter(&repositories.UserRepo{})

	//characterRepo目前只是用了数据库链接 所以可以每次都创建
	builder.Register(func(c ginjector.Container) interface{} {
		return repositories.NewCharacterRepo(utils.GetLogger("CharacterRepo"), engine)
	}).Adapter(&repositories.CharacterRepo{})

	builder.Register(func(c ginjector.Container) interface{} {
		return repositories.NewAnnouncementRepo(utils.GetLogger("Announcement"), engine)
	}).Adapter(&repositories.AnnouncementRepo{})

	builder.Register(func(c ginjector.Container) interface{} {
		return repositories.NewLogRepo(utils.GetLogger("LogRepo"), engine)
	}).Adapter(&repositories.LogRepo{})

	return builder
}

func startServer(config *Config, bd ginjector.Builder) {
	ow := owig.New(bd)
	log := utils.GetLogger("owmw")
	//添加中间件
	ow.Append(
		owmw.NewRecovery(log),
		owmw.NewLogger(log),
		owmw.NewRouter(),
		middle.NewCors(),
		middle.NewCheckSession("/Test", "/User/Register", "/Auth/Login", "/Announcement/All"),
		//需要用户登录但不需要角色登录
		middle.NewCheckLogined("/Auth/Logout", "/User/UpdatePassword", "/Character/All",
			"/Character/Create", "/Character/Switch"),
		owmw.NewParser(owmw.RouteTag, log),
		owmw.NewValidator(owmw.RouteTag, owmw.ContentTag))

	//配置路径
	router := ow.Get(2).(*owmw.Router)
	auth := &controllers.Authorize{ILogger: utils.GetLogger("Auth")}
	router.Post("/Auth/Login", auth.Login).Post("/Auth/Logout", auth.Logout)

	user := &controllers.User{ILogger: utils.GetLogger("User")}
	router.Post("/User/Register", user.Register).Post("/User/UpdatePassword", user.UpdatePassword)

	push := &controllers.Connection{ILogger: utils.GetLogger("Connection")}
	router.Get("/Connection/Connect", push.Connect)

	ch := &controllers.Character{ILogger: utils.GetLogger("Character")}
	router.Get("/Character/All", ch.All).Post("/Character/Create", ch.Create).Post(
		"/Character/Switch", ch.Switch).Post("/Character/Quit", ch.Quit).Get(
		"/Character/Detailed", ch.GetDetailedInfo).Post("/Character/SignIn", ch.SignIn).Post(
		"/Character/SignInPeriod", ch.SignInPeriod).Post("/Character/BuyPeriod", ch.BuyPeriod).Post(
		"/Character/Deposit", ch.Deposit)
	announce := &controllers.Announcement{ILogger: utils.GetLogger("Announcement")}
	router.Get("/Announcement/All", announce.GetAllAnnounce)

	test:= &controllers.Test{}
	router.Get("/Test", test.GetTest)

	//启动golang http server
	http.HandleFunc("/", ow.ServeHTTP)
	err := http.ListenAndServe(config.WebIP, nil)
	if err != nil {
		panic(err)
	}
}

func syncDB(config *Config) *xorm.Engine {
	//初始化数据库
	engine, err := xorm.NewEngine(config.DBDriver, config.DataSource)
	if err != nil {
		panic(err)
	}
	engine.SetMapper(core.SameMapper{})
	// tz, err := time.LoadLocation("Europe/London")
	// if err != nil {
	// 	panic(err)
	// }
	// engine.TZLocation = tz
	engine.ShowSQL(true)
	err = engine.Ping()
	if err != nil {
		panic(err)
	}
	err = models.Sync(engine)
	if err != nil {
		panic(err)
	}
	go func() {
		select {
		case <-time.After(3 * time.Hour):
			engine.Ping() //每3小时Ping一下 防止数据库链接超时
		}
	}()
	return engine
}

func loadConfig() (*Config, error) {
	config := &Config{}
	fi, err := os.Open(configPath)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	data, err := ioutil.ReadAll(fi)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(data, config)
	if err != nil {
		return nil, err
	}
	return config, err
}
